#include "tclient.h"
#include <iostream>
#include <QPushButton>
#include <QTextEdit>
#include <QLabel>
#include <QLineEdit>
#include <QDateTime>
#include <QDebug>
#include <json/json.h>
#include <QFileDialog>
#include <QMessageBox>

using namespace hv;

#define CLIENT_RECONNECT  1

TClient* TClient::Instance()
{
    static TClient instance;
    return &instance;
}

TClient::TClient(QWidget *parent)
    : QMainWindow(parent)

    , m_pStart(new QPushButton(this))
    , m_pEnd(new QPushButton(this))
    , m_pHost(new QLabel(this))
    , m_pPort(new QLabel(this))
    , m_pName(new QLabel(this))
    , m_pHostLine(new QLineEdit(this))
    , m_pPortLine(new QLineEdit(this))
    , m_pNameLine(new QLineEdit(this))
    , m_pTextEdit(new QTextEdit(this))
    , m_pSendButton(new QPushButton(this))
    , m_pSelectImgButton(new QPushButton(this))
    , m_pSendImgButton(new QPushButton(this))
    , m_pUploadPdfFileButton(new QPushButton(this))
    , m_HeartBeatLoop(new EventLoop)
{

    resize(500, 500);
    initUI();
    initConnect();
}

void TClient::initConnect()
{
    connect(m_pStart, &QPushButton::released, this , [=]() {
        m_pClient = new hv::TcpClient;
        std::string host = m_pHostLine->text().toStdString();
        int port = m_pPortLine->text().toInt();
        //emit setStartButtonEnables;
        if (connection(port, host.c_str()))
        {
            //m_pStart->setEnabled(false);m_pEnd->setEnabled(true);
            std::cout << "TCP client is connecting to " << host << "  " << port << std::endl;
        }
        else {
            std::cout << "TCP client failed connect to " << host << "  " << port << std::endl;
        }
        m_HeartBeatLoop = m_HeartBeatLoopThread.loop();
        m_HeartBeatLoop->setInterval(10000,[this](TimerID){
            if (m_pClient == nullptr || !m_pClient->isConnected())
            {
                std::cout << "Send HeartBeat Failed!!" << std::endl;
                failedHeartBeat++;
            }
            else {
                m_pClient->send(sendHeartBeat());
                std::cout << "heartbeat---" << std::endl;
            }
            if(failedHeartBeat >= 3)
            {
                std::cout << "---------------------" << std::endl;
                m_HeartBeatLoop->pause();

                /* QString message = QString("与服务器断开连接，请点击close关闭客户端进行重连");
                QMessageBox::question(nullptr, "Confirm", message, QMessageBox::Yes | QMessageBox::No);*/
            }
        });
        m_HeartBeatLoopThread.start();
    });

    connect(m_pEnd, &QPushButton::released, this , [=]() {

        Close();
        m_pStart->setEnabled(true);
        m_pEnd->setEnabled(false);
        std::cout << "TCP client closed" << std::endl;
    });

    connect(m_pSendButton, &QPushButton::released, this , [=]() {

        Json::Value receiver(Json::arrayValue);
        receiver.append("WEATHERMODULE");
        receiver.append("Client1");

        Json::Value content;
        content["weather"] = "清朗";
        content["humunity"] = "适中";
        content["data"] = m_pTextEdit->toPlainText().toStdString();
        
        Json::Value root;
        root["cmd"] = cmdToInt(cmd::SEND);
        root["sender"] = m_pNameLine->text().toStdString();
        root["receiver"] = receiver;
        root["content"] = content;
        root["time"] = QDateTime::currentDateTime().toString("hh:mm:ss").toStdString();

        Json::StreamWriterBuilder writer;
        std::string outputString = Json::writeString(writer, root);
        //长度头
        int jsonLength = outputString.length();
        //将整数转换为QByteArray，但需要注意字节序和网络协议
        QByteArray lengthHeader;
        QDataStream out(&lengthHeader, QIODevice::WriteOnly);
        out.setByteOrder(QDataStream::BigEndian); // 假设使用大端字节序
        out << static_cast<quint32>(jsonLength);

        if (m_pClient == nullptr || !m_pClient->isConnected())
            std::cout << "Client has no connect with Server!!" << std::endl;
        else {
            m_pClient->send(lengthHeader.toStdString() + outputString);
            m_pTextEdit->clear();
        }
    });

    connect(m_pSelectImgButton, &QPushButton::released, this, &TClient::onSelectImgButtonReleased);
    connect(m_pSendImgButton, &QPushButton::released, this, &TClient::onSendImgButtonReleased);
    connect(this, &TClient::setStartButtonEnables, this, [=]() {
        m_pStart->setEnabled(false);
        m_pEnd->setEnabled(true);
    });
    connect(this, &TClient::setCloseButtonEnables, this, [=]() {
        m_pStart->setEnabled(true);
        m_pEnd->setEnabled(false);
    });
       

    connect(m_pUploadPdfFileButton, &QPushButton::released, this , [=]() {
        QString fileName = QFileDialog::getOpenFileName(this,
            tr("select Pdf file"), "", tr("PDF Files (*.pdf)"));
        QByteArray fileData;//读取文件内容
        if (!fileName.isEmpty()) {
            selectedPdfFilePath = fileName;
            selectedPdfFileName = QFileInfo(fileName).fileName();
            QFile file(fileName);
            if (!file.open(QIODevice::ReadOnly)) {
                std::cout << "failed to open file" << std::endl;
            }
            fileData = file.readAll();
            file.close();
        }
        QString message = QString("The file is %1\nDo you want to upload the file?").arg(selectedPdfFilePath);
        int ret = QMessageBox::question(nullptr, "Confirm", message, QMessageBox::Yes | QMessageBox::No);
        if (ret == QMessageBox::Yes) {
            QByteArray base64Data = fileData.toBase64();
            QString base64String = QString::fromUtf8(base64Data);

            Json::Value root;
            root["cmd"] = TClient::Instance()->cmdToInt(cmd::SENDPDFFILE);
            root["content"] = base64String.toStdString();
            root["filename"] = selectedPdfFileName.toStdString();
            Json::StreamWriterBuilder writer;
            std::string outputstring = Json::writeString(writer, root);
            std::cout << outputstring << std::endl;

            //长度头
            int jsonLength = outputstring.length();
            //将整数转换为QByteArray
            QByteArray lengthHeader;
            QDataStream out(&lengthHeader, QIODevice::WriteOnly);
            out.setByteOrder(QDataStream::BigEndian); // 假设使用大端字节序
            out << static_cast<quint32>(jsonLength);

            std::cout << "发送了长度头: " << jsonLength << std::endl;
            std::cout << "传输了包含文件的数据" << std::endl;
            m_pClient->send(lengthHeader.toStdString()+ outputstring);
        }
        else {
            std::cout << "User chose not to send the file." << std::endl;
        }
    });
}

std::string TClient::sendHeartBeat()
{
    Json::Value root;
    root["cmd"] = cmdToInt(cmd::HEARTBEAT);
    root["name"] = m_pNameLine->text().toStdString();
    Json::StreamWriterBuilder writer;
    std::string outputString = Json::writeString(writer, root);
    //长度头
    int jsonLength = outputString.length();
    //将整数转换为QByteArray，但需要注意字节序和网络协议
    QByteArray lengthHeader;
    QDataStream out(&lengthHeader, QIODevice::WriteOnly);
    out.setByteOrder(QDataStream::BigEndian); // 假设使用大端字节序
    out << static_cast<quint32>(jsonLength);

    return lengthHeader.toStdString() + outputString;
}

void TClient::onSendJsonPdfFile(std::string outputString)
{

    if (m_pClient == nullptr || !m_pClient->isConnected()) {
        //m_pDisplayTextEdit->append("Please open first!");
        std::cout << "client has no connection with server" << std::endl;
        return;
    }
    if (outputString.empty())
    {
        std::cout << "所选文件为空" << std::endl;
        return;
    }
    std::cout << "发送了json字符串" << std::endl;
    m_pClient->send(outputString);
}

void TClient::onSelectImgButtonReleased()
{
    QString fileName = QFileDialog::getOpenFileName(this, "select image", "", "Images (*.png *.jpg *.bmp)");
    if (!fileName.isEmpty())
    {
        selectedImagePath = fileName;
        selectedImageName = QFileInfo(fileName).fileName();
        QPixmap pixmap(fileName);
        if (!pixmap.isNull())
        {
            QIcon icon(pixmap);
            m_pSelectImgButton->setIcon(icon);
            m_pSelectImgButton->setIconSize(m_pSelectImgButton->size());
        }
        else
            m_pSelectImgButton->setText("failed to load image");
    }
}

void TClient::onSendImgButtonReleased()
{
    if (m_pClient == nullptr || !m_pClient->isConnected()) {
        std::cout << "client has no connection with server" << std::endl;
        return;
    }
    if (!m_pSelectImgButton->icon().isNull()) {
        std::cout << "image is exist" << std::endl;
        Json::Value root;
        root["image"] = imageToString(selectedImagePath);
        root["cmd"] = cmdToInt(cmd::SENDIMG);
        root["image-name"] = selectedImageName.toStdString();
        Json::StreamWriterBuilder writer;
        std::string outputString = Json::writeString(writer, root);
        //长度头
        int jsonLength = outputString.length();
        //将整数转换为QByteArray，但需要注意字节序和网络协议
        QByteArray lengthHeader;
        QDataStream out(&lengthHeader, QIODevice::WriteOnly);
        out.setByteOrder(QDataStream::BigEndian); // 假设使用大端字节序
        out << static_cast<quint32>(jsonLength);

        m_pClient->send(lengthHeader.toStdString() + outputString);
    }
    else
        std::cout << "image is not exist" << std::endl;
}


bool TClient::connection(int port, const char* host)
{
    int connfd = m_pClient->createsocket(port, host);
    if (connfd < 0) {
        return false;
    }

    m_pClient->onConnection = [this](const hv::SocketChannelPtr& channel) {
        if (channel->isConnected()) {
            std::cout << "TCP client connected! connfd=" << channel->fd() << std::endl;
            emit setStartButtonEnables();
            sendInitInfo();
        }
        else {
            channel->close();
            std::cout << "TCP client disconnected! connfd=" << channel->fd() << std::endl;
            //emit setCloseButtonEnables();
            //return false;
        }
    };

    m_pClient->onMessage = [this](const hv::SocketChannelPtr& channel, hv::Buffer* buf) {
        std::string str = buf->base;
        std::cout << str << "from " << channel->fd() <<std::endl;
        if(str == "connection still maintained")
            failedHeartBeat = 0;
    };

#if CLIENT_RECONNECT
    // reconnect: 1,2,4,8,10,10,10...
    reconn_setting_t reconn;
    reconn_setting_init(&reconn);
    reconn.min_delay = 1000;
    reconn.max_delay = 10000;
    reconn.delay_policy = 2;
    m_pClient->setReconnect(&reconn);
#endif

    m_pClient->start();
    return true;
}

void TClient::close()
{
    SAFE_DELETE(m_pClient);
}

TClient::~TClient()
{
    close();
}



void TClient::initUI()
{
    m_pHost->move(50, 50);
    m_pPort->move(50, 100);
    m_pName->move(50, 0);
    m_pHost->setText("Host");
    m_pPort->setText("Port");
    m_pName->setText("Name");

    m_pHostLine->move(90, 50);
    m_pHostLine->setText("172.16.191.79");
    m_pPortLine->move(90, 100);
    m_pPortLine->setText("9999");
    m_pNameLine->move(90, 0);
    m_pNameLine->setText("Client1");

    m_pStart->setText("Connect");
    m_pEnd->setText("Cancel");
    m_pStart->move(50, 150);
    m_pEnd->move(200, 150);

    m_pTextEdit->move(20, 180);
    m_pTextEdit->resize(400, 200);

    m_pSendButton->move(430, 280);
    m_pSendButton->resize(60, 30);
    m_pSendButton->setText("Send");

    m_pSelectImgButton->resize(100, 100);
    m_pSelectImgButton->move(300, 20);

    m_pSendImgButton->move(430, 50);
    m_pSendImgButton->resize(50, 30);
    m_pSendImgButton->setText("Upload");

    m_pUploadPdfFileButton->move(420, 100);
    m_pUploadPdfFileButton->resize(80, 30);
    m_pUploadPdfFileButton->setText("UploadFile");
}

int TClient::cmdToInt(cmd command)
{
    return static_cast<int>(command);
}


void TClient::sendInitInfo() 
{
    //构建初始化连接的json字符串
    Json::Value root;
    root["cmd"] = cmdToInt(cmd::INITCONNECT);
    root["name"] = m_pNameLine->text().toStdString();
    Json::StreamWriterBuilder writer;
    std::string outputString = Json::writeString(writer, root);
    std::cout << outputString << std::endl;

    //长度头
    int jsonLength = outputString.length();
    //将整数转换为QByteArray，但需要注意字节序和网络协议
    QByteArray lengthHeader;
    QDataStream out(&lengthHeader, QIODevice::WriteOnly);
    out.setByteOrder(QDataStream::BigEndian); // 假设使用大端字节序
    out << static_cast<quint32>(jsonLength);
    if (m_pClient == nullptr || !m_pClient->isConnected())
        std::cout << "Send Initial Info Failed!!" << std::endl;
    else {
        m_pClient->send(lengthHeader.toStdString() + outputString);
        std::cout << "初始化信息发送完成" << std::endl;
    }
}

std::string TClient::imageToString(const QString& filename)
{
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly)) {
        std::cout << "Failed to open file:" << filename.toStdString() << std::endl;;
        return std::string();
    }
    QByteArray fileData = file.readAll();
    file.close();

    QByteArray base64Data = fileData.toBase64();
    QString base64String = QString::fromUtf8(base64Data);

    return base64String.toStdString();
}

void TClient::Close()
{
    m_HeartBeatLoop->pause(); //关闭心跳
    if (m_pClient == nullptr || !m_pClient->isConnected()) {
        //m_pDisplayTextEdit->append("Please open first!");
        std::cout << "client has no connection with server" << std::endl;
        return;
    }
    Json::Value root;
    root["cmd"] = cmdToInt(cmd::DISCONNECT);
    root["name"] = m_pNameLine->text().toStdString();
    Json::StreamWriterBuilder writer;
    std::string outputString = Json::writeString(writer, root);

    int jsonLength = outputString.length();
    //将整数转换为QByteArray，但需要注意字节序和网络协议
    QByteArray lengthHeader;
    QDataStream out(&lengthHeader, QIODevice::WriteOnly);
    out.setByteOrder(QDataStream::BigEndian); // 假设使用大端字节序
    out << static_cast<quint32>(jsonLength);

    m_pClient->send(lengthHeader.toStdString() + outputString);
    std::cout << "断联消息已发送" << std::endl;
    SAFE_DELETE(m_pClient);
    std::cout << "release m_pClient" << std::endl;
}
